
#pragma once

#ifndef _fmbList
#define _fmbList

#include "fmbPrimitive.h"
#include "fmbErr.h"
#include "fmbMutex.h"

namespace fm {
  namespace base {


class ListDefaults {
public:
  typedef ObjectLevelLockingPolicy LockingPolicy;
  typedef LockingPolicy::Lock Lock;
};

template<typename T, class NTP = ListDefaults>
class List {

  template<typename T_, class NTP_>
  friend class List;

  typedef typename NTP::LockingPolicy LockingPolicy;
  typedef typename NTP::Lock Lock;

private:
  static const int INITIAL_CAPACITY = 10;

  LockingPolicy m_lockingPolicy;

  T*    m_buf;
  int m_capacity;
  int m_num;

  void allocate(int capacity) {
    Lock lock(m_lockingPolicy);
    m_buf = new T[capacity];    
    m_capacity = capacity;
    m_num = 0;
  }

  void dellocate() {
    Lock lock(m_lockingPolicy);
    delete[] m_buf;
  }

  void reallocate(int newCapacity) {
    Lock lock(m_lockingPolicy);
    T *newBuf = new T[newCapacity];
    T *pn = newBuf;
    T *po = m_buf;
    for (int i = 0; i < m_num; ++i, ++pn, ++po) *pn = *po;
    delete[] m_buf;
    m_capacity = newCapacity;
    m_buf = newBuf;
  }

public:

  List(int initialCapacity = INITIAL_CAPACITY) :
      m_buf(new T[initialCapacity]), m_capacity(INITIAL_CAPACITY), m_num(0)
  {}

  template<typename T_, class NTP_>
  List(List<T_, NTP_> &source) {
    typename List<T_, NTP_>::Lock lock(source.m_lockingPolicy);
    allocate(source.capacity());
    T *ps = source.m_buf;
    T *p = m_buf;
    for (int i = 0; i < source.num(); ++i, ++ps, ++p) *p = *ps;
    m_num = source.num();
  }

  template<typename T_, class NTP_>
  List operator=(List<T_, NTP_> &source) {
    if (this != &source) {
      typename List<T_, NTP_>::Lock locks(source.m_lockingPolicy);
      Lock lockt(m_lockingPolicy);
      if (capacity() < source.num()) reallocate(source.capacity());
      T *ps = source.m_buf;
      T *p = m_buf;
      for (int i = 0; i < source.num(); ++i, ++ps, ++p) *p = *ps;   
      m_num = source.num();
    }
    return *this;
  }

  ~List() {
    Lock lock(m_lockingPolicy);
    dellocate();
  }

  inline void add(const T &elm) {
    Lock lock(m_lockingPolicy);
    if (m_num == m_capacity) reallocate(m_capacity * 2);
    m_buf[m_num++] = elm;
  }

  inline void remove(int index) {
    Lock lock(m_lockingPolicy);
    if (index >= m_num) throw ArrayErr("List: array index out of bound");
    for (int i = index; i < m_num - 1; ++i) m_buf[i] = m_buf[i + 1];
    --m_num;
  }

  inline T operator[](int index) {
    Lock lock(m_lockingPolicy);
    // check necessary, because of multithreading
    if (index >= m_num) throw ArrayErr("List: array index out of bound");
    return m_buf[index];
  }

  inline const int num() {
    Lock lock(m_lockingPolicy);
    return m_num;
  }

  inline const int capacity() {
    Lock lock(m_lockingPolicy);
    return m_capacity;
  }
};

  // end of namespace fm::base
  }
// end of namespace fm
}

#endif
